package org.budo.netty.servlet.server.connector.netty;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Set;

import javax.servlet.ServletInputStream;
import javax.servlet.http.Cookie;

import org.budo.netty.servlet.server.connector.AbstractHttpConnector;
import org.budo.netty.servlet.server.connector.HttpConnector;
import org.budo.netty.servlet.server.handler.BudoServletHandler;
import org.budo.netty.servlet.server.handler.servlet.AbstractBudoServletRequest;
import org.budo.support.javax.servlet.BudoServletInputStream;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.cookie.ServerCookieDecoder;

/**
 * @author li
 */
public class NettyServletRequest extends AbstractBudoServletRequest {
    private ChannelHandlerContext handlerContext;

    private FullHttpRequest nettyRequest;

    public NettyServletRequest(FullHttpRequest nettyRequest, BudoServletHandler servletHandler, ChannelHandlerContext handlerContext) {
        this.setBudoServletHandler(servletHandler);

        this.handlerContext = handlerContext;
        this.nettyRequest = nettyRequest;
    }

    /**
     * requestURI 不包含 queryString
     */
    @Override
    public String getRequestURI() {
        String uri = this.nettyRequest.uri();
        int index = uri.indexOf('?');
        if (index == -1) {
            return uri;
        }

        return uri.substring(0, index);
    }

    @Override
    public String getQueryString() {
        String uri = this.nettyRequest.uri();
        int index = uri.indexOf('?');
        if (index == -1) {
            return null;
        }

        return uri.substring(index + 1);
    }

    @Override
    public String getHeader(String name) {
        return this.nettyRequest.headers().get(name);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        ByteBuf byteBuf = this.nettyRequest.content();
        String requestBody = NettyUtil.byteBufToString(byteBuf);
        return new BudoServletInputStream(requestBody);
    }

    @Override
    public String getMethod() {
        return this.nettyRequest.method().name();
    }

    @Override
    public String getRemoteAddr() {
        SocketAddress remoteAddress = this.handlerContext.channel().remoteAddress();
        InetSocketAddress inetSocketAddress = (InetSocketAddress) remoteAddress;
        return inetSocketAddress.getAddress().getHostAddress();
    }

    @Override
    public int getRemotePort() {
        SocketAddress remoteAddress = this.handlerContext.channel().remoteAddress();
        InetSocketAddress inetSocketAddress = (InetSocketAddress) remoteAddress;
        return inetSocketAddress.getPort();
    }

    @Override
    public Cookie[] getCookies() {
        String headerValue = this.nettyRequest.headers().get(HttpHeaderNames.COOKIE);
        if (null == headerValue || headerValue.isEmpty()) {
            return new Cookie[0];
        }

        Set<io.netty.handler.codec.http.cookie.Cookie> nettyCookies = ServerCookieDecoder.STRICT.decode(headerValue);
        Cookie[] cookies = new Cookie[nettyCookies.size()];

        int i = 0;
        for (io.netty.handler.codec.http.cookie.Cookie nettyCookie : nettyCookies) {
            cookies[i] = new Cookie(nettyCookie.name(), nettyCookie.value());
            i++;
        }

        return cookies;
    }

    @Override
    public Enumeration<String> getHeaderNames() {
        return Collections.enumeration(this.nettyRequest.headers().names());
    }

    @Override
    public Enumeration<String> getHeaders(String name) {
        return Collections.enumeration(this.nettyRequest.headers().getAll(name));
    }

    @Override
    public String getProtocol() {
        return "HTTP/1.1";
    }

    @Override
    public int getServerPort() {
        HttpConnector httpConnector = this.getBudoServletHandler().getBudoServletServer().getHttpConnector();
        return ((AbstractHttpConnector) httpConnector).getPort();
    }

    @Override
    public String getLocalName() {
        InetSocketAddress localAddress = (InetSocketAddress) this.handlerContext.channel().localAddress();
        return localAddress.getHostString();
    }

    @Override
    public String toString() {
        return super.toString() + ", requestURI=" + this.getRequestURI();
    }
}